# -*- coding: utf-8 -*-
import os, sys
import collections
import requests
import shutil
from shutil import copyfile
import time
from Crypto.Cipher import AES  # pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pycryptodome
from bs4 import BeautifulSoup  # pip install beautifulsoup4  中文文档：https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/


# 创建目录
def mkDir(path):
    """
    创建目录
    :param path: 目录
    :return:
    """
    # 去除首位空格
    path = path.strip()
    # 去除尾部 \\ / 符号
    path = path.rstrip("\\")
    path = path.rstrip("/")
    # 判断路径是否存在
    isExists = os.path.exists(path)
    # 判断结果
    if not isExists:
        # 如果不存在则创建目录
        os.makedirs(path)
    return True

# 创建一个文件并写入内容
def mkFile(path,text):
    """
    创建一个文件并写入内容
    :param path: E:/file/a.txt
    :param text: 我是一个文件的内容
    :return: 成功状态
    """
    f = open(path, "w")
    f.write(text)
    f.close()
    return True

# 删除某个目录下的所有文件夹和文件
def killdirData(path):
    """
    删除某个目录下的所有文件夹和文件
    :param path: E:mp4/demo
    """
    ls = os.listdir(path)
    for i in ls:
        c_path = os.path.join(path, i)
        if os.path.isdir(c_path):
            shutil.rmtree(c_path)
        else:
            os.remove(c_path)

# 删除指定目录下的某个文件
def removeFile(path):
    """
    删除指定目录下的某个文件
    :param path: E:mp4/a.txt
    :return:
    """
    if os.path.exists(path):
        os.remove(path)

# 复制某个文件到另一个目录
def copyFile(path1,path2):
    """
    复制某个文件到另一个目录
    :param path1: E:mp4/a.txt
    :param path2: D:mp3/jay/a.txt
    :return: 成功状态
    """
    copyfile(path1, path2)

# 修改文件名
def exitFileName(path, name):
    """
    修改文件名
    :param path: E:mp4/a.txt
    :param name: b.txt
    :return:
    """
    path = path.replace("\\", "/")
    path2 = path.split(path.split("/")[-1])[0]+"/"
    os.rename(path, path2 + name)

# 通过电影名获取html
def getHtmlByName(moviename):
    header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
                            " (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36"}
    print("正在搜索"+moviename+"的相关网页信息...")
    try:
        url = "https://www.vodsee.com/search.html?searchword=" + moviename
        r = requests.get(url, headers=header, timeout=15)
        r.raise_for_status()
        # r.status_code
        r.encoding = r.apparent_encoding
        soup = BeautifulSoup(r.text, "html.parser")
        # print(soup.prettify())  # 打印解析之后的网页内容
        # print("--------------------------------------------")
        map = collections.OrderedDict()
        for line in soup.find_all('a', "videoName"):  # 获取css为videoName的a标签
            name = line.string.strip().replace('\xa0','').replace('\r','').replace('\n','')
            gj = line.parent.find('span', "region").string
            lx = line.parent.find('span', "category").string
            map[name + "_" + gj + "_" + lx] = "https://www.vodsee.com" + line["href"]
        print("搜索成功")
        return map
    except:
        print(moviename+" 网页信息获取失败")
        return None

# 通过解析网页获取m3u8链接地址
def getM3u8ByHtml(url):
    header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
                            " (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36"}
    print("正在解析网页")
    try:
        r = requests.get(url, headers=header, timeout=15)
        r.raise_for_status()
        # r.status_code
        r.encoding = r.apparent_encoding
        soup = BeautifulSoup(r.text, "html.parser")
        # print(soup.prettify())  # 打印解析之后的网页内容
        tag = soup.find(id="m3u8")
        m3u8url = tag["value"].split("$")[1]
        print("网页解析完毕，链接已获取： " + m3u8url)
        return m3u8url
    except:
        print("网页无法解析，m3u8链接获取失败")
        return False

# 检测m3u8文件是否需要解密
def fileIfDecrypt(path):
    """
    检测m3u8文件是否需要解密
    :param path: E:mp4/a.m3u8
    :return: 需要解密返回key链接路径，不需要解密就返回False
            /20190525/GPlWuHfM/1561kb/hls/key.key
    """
    print("正在检测是否需要解密...")
    with open(path) as f:
        for line in f:
            if "EXT-X-KEY" in line:
                key_path = line[line.find("URI"):line.rfind('"')].split('"')[1]
                f.close()
                if key_path[0] is "/":
                    return key_path
                else:
                    return "/" + key_path
    f.close()
    return False

# 下载任何格式的网络文件
def downloadFile(url,filepath):
    """
    下载任何格式的网络文件
    :param url: 文件的链接地址   http://cn5.download05.com/hls/name.txt
    :param filepath: 存放的本地路径包含文件名 E:mp4/a.txt
    :return: 返回成功状态和耗时(秒)
    """
    if os.path.exists(filepath):
        return "True,0.300"
    else:
        try:
            header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
                                    " (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36"}

            st = int(round(time.time() * 1000))
            r = requests.get(url, headers=header, timeout=15)
            # r.raise_for_status()
            if r.status_code == 200:
                with open(filepath, "wb") as f:  # wb：以二进制方式写入文件，如果文件存在会覆盖
                    f.write(r.content)  # r.content：以二进制方式读取文件,然后写入
                f.close()
                et = int(round(time.time() * 1000))
                ht = str((et-st)/1000)  # 耗时
                return "True," + ht
            else:
                return "False,0.300"
        except:
            return "False,0.300"

# 下载列表里所有的ts文件
def downByList(list, localpath ,key=None, list2=None):
    """

    :param list: 存放ts文件的列表
    :param localpath: 下载目录
    :param key: 秘钥
    :param list2: 排除列表（下载这个列表里没有的ts文件） 通过文件名的后6位数字排处
    :return:
    """
    num = 0
    length = len(list)
    isall = True
    t2 = None
    for i in list:
        num = num + 1
        tsfilename = localpath + "/" + str(100 + num) + "_" + str(i.split("/")[-1]).split(".ts")[0] + ".ts"
        if list2 is not None:
            if i[-9:] not in list2:
                if key is None:
                    t2 = downloadFile(i, tsfilename)
                else:
                    t2 = downloadTsFileAndDecrypt(i, tsfilename, key)
            else:
                print(tsfilename + "已下载")
                continue
        else:
            if key is None:
                t2 = downloadFile(i, tsfilename)
            else:
                t2 = downloadTsFileAndDecrypt(i, tsfilename, key)
        if "True" in t2:
            sy = length - num  # 剩余多少个
            htime = round(float(t2.split(",")[1]) * sy / 60, 2)
            print(tsfilename + "下载成功，总共" + str(length) + "个，剩余" + str(sy) + "个, 预计需要 " + str(htime) + " 分钟")
        else:
            isall = False
            print(tsfilename + "下载失败")
    if isall is True:
        print("ts文件全部下载完成，开始合并ts文件")
        return True
    else:
        return False

# 获取秘钥
def getKey(m3u8url,keyurl,keypath):
    """
    获取秘钥
    :param m3u8url: m3u8有效连接地址 http://cn5.download05.com/hls/20190525/index.m3u8
    :param keyurl:  /20190525/GPlWuHfM/1561kb/hls/key.key
    :param keypath: key文件存放目录 E:mp4/a
    :return: 秘钥字符串
    """
    zt = False
    print("正在获取秘钥...")
    list = []
    list.append(m3u8url.split("//")[0]+"//"+m3u8url.split("//")[1].split("/")[0])
    list.append(m3u8url[0:m3u8url.rfind("/")])

    keypath = keypath+"/"+keyurl[keyurl.rfind("/"):]

    for u1 in list:
        ulist = u1.split("/")
        klist = keyurl.split("/")
        for k1 in klist:
            if (k1 in ulist) and k1 != '':
                u1 = u1.split(k1)[0]
                break
        url = u1 + keyurl

        removeFile(keypath)  # 下载前先删除，确保此文件不存在，防止文件失效而一直下载不到
        t1 = downloadFile(url, keypath)
        if "True" in t1:
            f = open(keypath, "r")
            text = f.read().strip("\n").strip()
            f.close()
            zt = True
            print("秘钥获取成功: " + text)
            #  保存key
            mkFile(keypath, text)
            return text
    if zt is False:
        print("获取秘钥失败")
    return zt

# 下载ts文件并解密
def downloadTsFileAndDecrypt(url,filepath,key):
    """
    下载ts文件并解密
    :param url:  https://3atvplay.com/20190801/HRraP1tT/1259kb/hls/asd123.ts
    :param path: E:/mp4/a.ts
    :param key:  508630799be9fc8a
    :return:     成功状态
    """
    if os.path.exists(filepath):
        return "True,0.300"
    else:
        try:
            header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
                                    " (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36"}

            key = bytes(key, encoding="utf8")
            cryptor = AES.new(key, AES.MODE_CBC, key)

            st = int(round(time.time() * 1000))
            r = requests.get(url, headers=header, timeout=15)
            r.raise_for_status()
            with open(filepath, "wb") as f:  # wb：以二进制方式写入文件，如果文件存在会覆盖
                f.write(cryptor.decrypt(r.content))
            f.close()
            et = int(round(time.time() * 1000))
            ht = str((et-st)/1000)  # 耗时
            return "True," + ht
        except:
            return "False,0.300"

# 解密本地ts文件
def decryptLocalTsFile(path,key):
    """
    解密本地ts文件
    :param path: E:\\mp4\\a.ts
    :param key:  508630799be9fc8a
    :return:     状态
    """
    try:
        # 在当前目录下创建一个新文件，存放解析后的新文件
        path = path.replace("\\","/")
        path2 = path.split(path.split("/")[-1])[0] + "jiexi"
        newfilename = path2 + "/" + path.split("/")[-1]
        mkDir(path2)

        key = bytes(key, encoding="utf8")
        cryptor = AES.new(key, AES.MODE_CBC, key)
        a = open(path, "rb")
        with open(newfilename, "wb") as f:  # wb：以二进制方式写入文件，如果文件存在会覆盖
            f.write(cryptor.decrypt(a.read()))
        a.close()
        f.close()
        print("解密成功 "+path)
        return True
    except:
        print("解密文件失败 " +path)
        return False

# 下载m3u8文件到指定目录，返回可直接拼接ts文件名的有效链接，m3u8存放路径
def downM3u8(url,path):
    """
    根据链接地址下载m3u8文件到指定目录
    :param url: http://cn5.download05.com/hls/20190812/182d4d695b9050c77f140bc0b21fbe9e/1565606686/index.m3u8
    :param path: E:mp4/hongfanqu/index.m3u8  （如果目录不存在不会新建目录）
    :return: 可直接拼接ts文件名的有效链接，m3u8存放目录
             http://cn5.download05.com/hls/20190812,E:mp4/hongfanqu/index
    """
    print("正在下载m3u8文件，请稍后...")
    t = downloadFile(url, path)
    if "True" in t:
        # ts文件链接地址拼接方案
        list = []
        list.append(url.split("/")[0] + "//" + url.split("/")[1] + url.split("/")[2])
        list.append(url.split("/index.m3u8")[0])

        iscg = True
        for url2 in list:
            with open(path) as f:
                for line in f:
                    if "index.m3u8" in line:
                        if line[0] is "/":
                            url3 = url2 + (line.split('\n')[0])
                        else:
                            url3 = url2 + "/" + (line.split('\n')[0])
                        path2 = path.split("index.m3u8")[0] + "index"
                        mkDir(path2)
                        t2 = downloadFile(url3, path2 + "/index.m3u8")
                        if "True" in t2:
                            print(path2 + "/index.m3u8 文件下载成功")
                            f.close()
                            return url3.split("/index.m3u8")[0] + "," + path2
                        else:
                            iscg = False
                            print("m3u8文件下载失败，正在重新组装链接...")
                if iscg is True:
                    f.close()
                    break
        print(path + " 文件下载成功")
        return url.split("/index.m3u8")[0] + "," + path.split("/index.m3u8")[0]
    else:
        print(path + " 文件下载失败")
        return False

# 解析m3u8文件返回ts链接地址 （列表）
def analysisM3u8File(path, url, startnum=None, endnum=None):
    """
    解析m3u8文件返回ts链接地址 （列表）
    :param path: m3u8文件地址 E:mp4/hongfanqu/index.m3u8
    :param url:  有效的头链接  http://cn5.download05.com/100kb/hls
    :return: ts链接列表
    """
    #  去除链接地址中多余的//
    url = url[:11] + url[11:].replace("//", "/")
    print("正在解析m3u8文件： " + path)
    urls = []

    isadd = False  # 是否可以添加
    #
    # todo 我觉得下面的代码还可以优化
    with open(path, "r") as file:
        for line in file:
            if (startnum is None) and (endnum is None):
                # 可能直接是ts结尾 也可能后面携带一些参数
                if line.endswith(".ts\n") or ".ts" in line:
                    if line.startswith("http"):
                        urls.append(line.strip("\n"))
                    else:
                        list = line.split("/")
                        for i in list:
                            if (i in url) and i != '':
                                line = line.split(i)[1]
                        if line[:1] is "/":
                            urls.append(url + line.strip("\n"))
                        else:
                            urls.append(url + "/" + line.strip("\n"))
            else:
                if line.endswith(startnum + ".ts\n"):
                    isadd = True
                elif line.endswith(endnum + ".ts\n"):
                    list = line.split("/")
                    for i in list:
                        if (i in url) and i != '':
                            line = line.split(i)[1]
                    if line[:1] is "/":
                        urls.append(url + line.strip("\n"))
                    else:
                        urls.append(url + "/" + line.strip("\n"))
                    break

                if isadd is True and line.endswith(".ts\n"):
                        list = line.split("/")
                        for i in list:
                            if (i in url) and i != '':
                                line = line.split(i)[1]
                        if line[:1] is "/":
                            urls.append(url + line.strip("\n"))
                        else:
                            urls.append(url + "/" + line.strip("\n"))

    file.close()
    if len(urls) <= 0:
        return None
    print("解析完毕，ts链接地址列表已生成")
    return urls

def getTsNameList(url, sn, en):
    """
    根据ts链接和开始号以及结束号，计算出需要下载的所有ts文件的连接
    :param url: http://cn4.download05.com/hls/20190727/0248d8f0033b991444d671fea2a42c57/1564205511/film_00018.ts
    :param sn: 0050
    :param en: 0100
    :return: list
    """
    sz = None  # 文件名数字部分
    zm = None  # 文件名字母部分
    s = url[url.rfind("/")+1:].split(".")[0]
    for index, i in enumerate(reversed(s)):
        if not i.isdigit():
            zm = s[:-index]
            sz = s[-index:]
            break

    list = []
    for i in range(int(sn)-1, int(en)):
        nsz = str(i+1)
        for j in range(len(sz)):
            if len(nsz) < len(sn):
                nsz = "0" + nsz
            else:
                break
        list.append(url[:url.rfind("/")]+"/" + zm + sz[:-len(nsz)] + nsz + ".ts")

    return list

# 根据ts链接地址，下载部分ts文件
def downSomeTs(url,st,en,path):
    """
    根据ts链接地址，下载部分ts文件
    :param url: ts文件链接地址
    :param st: ts文件开始编号
    :param en: ts文件结束编号
    :param path: 本地存放路径
    :return: 成功状态
    """

    print("正在下载ts文件，请稍后...")

    tsnamelist = getTsNameList(url, st, en)

    # u2 = url.split(".ts")[0].split("/")[-1]
    # url1 = url.split(u2)[0]
    # https://dadi-yun.com/20190615/9821_64e278a8/800k/hls/9yEN6OZ8959998.ts

    length = int(en)-int(st)+1  # 文件个数
    ishb = True
    num = 0

    for i in tsnamelist:
        num = num+1
        path2 = i[i.rfind("/")+1:]
        t = downloadFile(i, path+"/"+path2)
        if "True" in t:
            sy = length - num  # 剩余多少个
            htime = round((float(t.split(",")[1]) * sy) / 60, 2)
            print(path + "/" + path2 + " 下载成功, 总共"+str(length)+"个，剩余"+str(sy)+"个, 预计需要 "+str(htime)+" 分钟")
        else:
            ishb = False
            print(path + "/" + path2 + " 下载失败")

    if ishb is True:
        return True
    else:
        return False

# 合并文件
def hbFile(localpath):
    """
    合并目录下所有的ts文件
    :param localpath: E:mp4/a/
    :return: new.ts
    """
    print(localpath+" 正在合并文件")
    filename = localpath + "/ALL_" + str(int(time.time())) + ".ts"
    try:
        newfile = open(filename, mode="ab")
        a = os.listdir(localpath)
        for i in a:
            if ("ts" in i) and ("ALL_" not in i):
                f = open(localpath + "/" + i, mode="rb")
                newfile.write(f.read())
                f.close()
        newfile.close()
        print("文件合并成功")
        #  转码
        # tsToMp4(filename)
        return filename
    except:
        print(localpath+" 合并失败")
        return False

# 视频转码
def tsToMp4(tspath):
    """
    ts文件转MP4文件
    :param tspath: E:mp4/1/a.ts
    :return:
    """
    print("开始转码...")
    try:
        tspath = tspath.replace("\\", "/").replace("\"", "/")
        mp4name = tspath.split(".")[0] + ".mp4"
        # path = os.path.abspath(os.path.dirname(sys.argv[0]))
        # exepath = path+"/ffmepeg/bin/ffmpeg.exe"
        cmd = "ffmpeg.exe -i " + tspath + " -c copy " + mp4name
        os.system(cmd)
        print("转码完成: " + mp4name)
        return True
    except:
        print("转码失败")
        return False
